﻿Type.registerNamespace('TallComponents');

TallComponents.ThumbnailsList = function (element) {
   ///<summary>Displays a list of autosizing PDF thumbnails</summary>
   TallComponents.ThumbnailsList.initializeBase(this, [element]);
   this._baseUrl = null;
   this._pageCount = 0;
   this._targetControlId = null;
   this._activeCssClass = null;
   this._activeIndex = -1;
   this._targetControl = null;
   this._thumbs = null;
   this._thumbLoaded = null;
   this._loadQueue = null;
   this._loading = null;
   this._appLoadDelegate = null;
   this._lastScrollTop = null;
   this._delayTimer = null;
   this._autoScaleMode = 'none';
   this._fixElementPos = false;
   this._scale = 0.25;
   this._storageProviderName = null;
};

//
// Create the prototype for the control.
//
TallComponents.ThumbnailsList.prototype = {
   initialize: function () {
      ///<summary>Initializes the control.</summary>
      ///<remarks>The initialize method initializes the control and sets the base <see cref="M:J#Sys.Component.isInitialized"/> property to true.</remarks>
      TallComponents.ThumbnailsList.callBaseMethod(this, "initialize");

      this._fixElementPos = (document.documentElement["getBoundingClientRect"]) ? true : false;
      this._findPosition = this._fixElementPos ? this._findPositionNew : this._findPositionOld;

      this._thumbs = new Array();
      this._thumbUrls = new Array();

      var element = this.get_element();
      var ulElement = element.getElementsByTagName('ul')[0];
      var images = ulElement.getElementsByTagName('img');
      for (var i = 0; i < images.length; i++) {
         images[i].pageIndex = i;
         images[i].rotation = 0;
         this._thumbs.push(images[i]);
      }

      this._pageCount = this._thumbs.length;

      $addHandlers(element, { scroll: this._onScroll }, this);

      if (!this._hookupViewer()) {
         this._appLoadDelegate = Function.createDelegate(this, this._appLoad);
         Sys.Application.add_load(this._appLoadDelegate);
      }
   },

   dispose: function () {
      ///<summary>Removes the current control from the application.</summary>
      ///<remarks>The dispose method releases all resources from the <see cref="T:J#Sys.UI.Control"/> object, 
      ///unbinds it from its associated HTML Document Object Model (DOM) element, 
      ///and unregisters it from the application.</remarks>
      $clearHandlers(this.get_element());
      if (this._targetControl) { this._targetControl.remove_updated(this._onViewerUpdated); }
      if (this._appLoadDelegate) { Sys.Application.remove_load(this._appLoadDelegate); }
      if (this._loading) { $clearHandlers(this._loading); }
      TallComponents.ThumbnailsList.callBaseMethod(this, 'dispose');
   },

   _appLoad: function (sender, args) {
      Sys.Application.remove_load(this._appLoadDelegate);
      this._appLoadDelegate = null;
      this._hookupViewer();
   },

   _hookupViewer: function () {

      this._targetControl = $find(this._targetControlId);
      if (this._targetControl) {
         this._targetControl.add_updated(Function.createDelegate(this, this._onViewerUpdated));
         return true;
      }
      setTimeout(Function.createDelegate(this, this._hookupViewer), 500);
      return false;
   },

   _onScroll: function (e) {
      if (this._delayTimer) {
         clearTimeout(this._delayTimer);
      }
      this._delayTimer = setTimeout(Function.createDelegate(this, this._startUpdate), 500);
   },

   _onViewerUpdated: function () {
      ///<summary>Handles the viewer update event.</summary>
      //we need to have a current page index and a current layout mode

      var currentPageIndex = this._targetControl.get_pageIndex();
      var pageLayout = this._targetControl.get_pageLayout();

      //      if (this._targetControl._document.IsValid) {
      //         var image = document.getElementById("thmbblankimage123qwe");
      //         //alert(image.style.visibility);
      //         image.style.display = "none";
      //      }

      //update the thumbnails taking into account a current layout mode
      switch (pageLayout) {
         case 'double':
            //only two pages at the viewer were updated, so lets update their thumbnails
            //the left page of a couple of the pages always has the even index
            var leftIndex = (currentPageIndex % 2 === 0) ? currentPageIndex : currentPageIndex - 1;
            //the right page of a couple of the pages has the next index
            var rightIndex = leftIndex + 1;

            this._updateThumbnailRotation(leftIndex);
            this._updateThumbnailRotation(rightIndex);
            break;
         case 'continuous':
         case 'continuousDouble':
            //all the pages were updated, so we need to update all the thumbnails
            if (this._targetControl._document.IsValid) {
               for (var pageIndex = 0; pageIndex < this._targetControl._document.Pages.length; pageIndex++) {
                  this._updateThumbnailRotation(pageIndex);
               }
            }
            break;
         default:
            //actually it means that the layout mode is 'single' and we update only the current page
            this._updateThumbnailRotation(currentPageIndex);
            break;
      }


      this._startUpdate();
   },

   _updateThumbnailRotation: function (thumbnailIndex) {
      ///<summary>Updates the rotation of a thumbnail with the specified index</summary>
      var thumbnail = this._thumbs[thumbnailIndex];
      if (thumbnail) {
         thumbnail.rotation = this._targetControl.getPageRotation(thumbnailIndex);
      }
   },

   _startUpdate: function () {
      if (this._delayTimer) {
         clearTimeout(this._delayTimer);
      }

      var thumb;
      var pos;
      var element = this.get_element();
      var containerPos = this._findPosition(element);
      containerPos.top = containerPos.top + element.scrollTop;
      containerPos.left = containerPos.left + element.scrollLeft;
      containerPos.bottom = containerPos.top + element.clientHeight;
      containerPos.right = containerPos.left + element.clientWidth;

      if (!this._loadQueue) {
         this._loadQueue = new Array();
      }

      // hit detection
      if (this._thumbs) {
         for (var i = 0; i < this._thumbs.length; i++) {
            thumb = this._thumbs[i];
            if (!this._isLoaded(thumb)) {
               pos = this._findPosition(thumb);
               if (this._fixElementPos) {
                  // apply fix for scroll position for newer findPosition implementation
                  pos.left += element.scrollLeft;
                  pos.top += element.scrollTop;
               }
               if ((pos.top + thumb.clientHeight) >= containerPos.top && pos.top < containerPos.bottom &&
                       (pos.left + thumb.clientWidth) >= containerPos.left && pos.left < containerPos.right) {
                  this._loadQueue.push(thumb);
               }
            }
         }
      }

      // make sure the correct thumb is highlighted
      if (this._thumbs && this._thumbs.length > 0 && this._activeCssClass) {
         if (this._activeIndex != this._targetControl.get_pageIndex()) {
            if (this._activeIndex >= 0 && this._activeIndex < this._thumbs.length) {
               Sys.UI.DomElement.removeCssClass(this._thumbs[this._activeIndex].parentNode, this._activeCssClass);
            }

            this._activeIndex = this._targetControl.get_pageIndex();

            thumb = this._thumbs[this._activeIndex];
            pos = this._findPosition(thumb);
            if (this._fixElementPos) {
               // apply fix for scroll position for newer findPosition implementation
               pos.left += element.scrollLeft;
               pos.top += element.scrollTop;
            }

            pos.bottom = pos.top + thumb.clientHeight;
            var isThumbFitVertically = pos.top >= containerPos.top && pos.bottom < containerPos.bottom;
            pos.right = pos.left + thumb.clientWidth;
            var isThumbFitHorisontally = pos.left >= containerPos.left && pos.right < containerPos.right;

            //calculate the vector delta that should be added to thumb position to get it fully visible
            var delta = { x: 0, y: 0 };

            if (!isThumbFitVertically) {
               //calculate Y component
               //if thumb's top is above the container's top 
               if (pos.top < containerPos.top) {
                  //thumb.top - container.top
                  delta.y = pos.top - containerPos.top;
               }
               else if (containerPos.bottom < pos.bottom) {
                  //if thumb's bottom is below the bottom point of the container = thumb.bottom - container.bottom
                  delta.y = pos.bottom - containerPos.bottom;
               }
            }

            if (!isThumbFitHorisontally) {
               //calculate X component
               //if thumb's left is behind the left border of the container
               if (pos.left < containerPos.left) {
                  //thumb.left - container.left
                  delta.x = pos.left - containerPos.left;
               }
               else if (containerPos.right < pos.right) {
                  //if thumb's right is behind the right border of the container = thumb.right - continer.right
                  delta.x = pos.right - containerPos.right;
               }
            }

            if (!(isThumbFitVertically && isThumbFitHorisontally)) {
               //add this delta to curretn pos and scroll to the new position
               var newpos = { x: element.scrollLeft + delta.x, y: element.scrollTop + delta.y }; //containerPos.top 
               this._scrollToThumbnail(newpos);
            }

            Sys.UI.DomElement.addCssClass(this._thumbs[this._activeIndex].parentNode, this._activeCssClass);
         }
      }

      this._loadNext();
   },

   _scrollToThumbnail: function (pos) {
      this.set_position(pos);
   },

   update: function (position) {
      ///<summary>Updates the thumbnails.</summary>
      this._startUpdate();
   },

   set_position: function (position) {
      ///<summary>Sets the position of the scrollbars.</summary>
      this.set_positionX(position.x);
      this.set_positionY(position.y);
   },

   set_positionX: function (xPosition) {
      ///<summary>Sets the position of the horisontal scrollbar.</summary>
      var container = this.get_element();
      container.scrollLeft = xPosition;
   },

   set_positionY: function (yPosition) {
      ///<summary>Sets the position of the vertical scrollbar.</summary>
      var container = this.get_element();
      container.scrollTop = yPosition;
   },

   _innerSize: function (elem) {
      var size = new Object();
      size.width = elem.clientWidth - this._safeParseInt(this._getStyle(elem, 'padding-left')) - this._safeParseInt(this._getStyle(elem, 'padding-right'));
      size.height = elem.clientHeight - this._safeParseInt(this._getStyle(elem, 'padding-top')) - this._safeParseInt(this._getStyle(elem, 'padding-bottom'));
      return size;
   },

   _chromeHeight: function (elem) {
      return this._safeParseInt(this._getStyle(elem, 'margin-top')) + this._safeParseInt(this._getStyle(elem, 'margin-bottom')) +
               this._safeParseInt(this._getStyle(elem, 'border-top-width')) + this._safeParseInt(this._getStyle(elem, 'border-bottom-width')) +
               this._safeParseInt(this._getStyle(elem, 'padding-top')) + this._safeParseInt(this._getStyle(elem, 'padding-bottom'));
   },

   _chromeWidth: function (elem) {
      return this._safeParseInt(this._getStyle(elem, 'margin-left')) + this._safeParseInt(this._getStyle(elem, 'margin-right')) +
               this._safeParseInt(this._getStyle(elem, 'border-left-width')) + this._safeParseInt(this._getStyle(elem, 'border-right-width')) +
               this._safeParseInt(this._getStyle(elem, 'padding-left')) + this._safeParseInt(this._getStyle(elem, 'padding-right'));
   },

   // See also http://ejohn.org/blog/getboundingclientrect-is-awesome/
   _findPositionNew: function (elem) {
      if (!elem) { return { top: 0, left: 0 }; }
      var box = elem.getBoundingClientRect();
      return { top: box.top, left: box.left };
   },

   _findPositionOld: function (obj) {
      var top = 0, left = 0;
      if (obj.offsetParent) {
         do {
            left += obj.offsetLeft;
            top += obj.offsetTop;
            obj = obj.offsetParent;
         } while (obj);
      }
      return { top: top, left: left };
   },

   _safeParseInt: function (raw) {
      var result = parseInt(raw);
      if (isNaN(result)) {
         result = 0;
      }
      return result;
   },

   _getStyle: function (el, styleProp) {
      var y = '';
      if (el.currentStyle) {
         var fixedProp = '';
         for (var i = 0; i < styleProp.length; i++) {
            if (styleProp.charAt(i) == '-') {
               i++;
               fixedProp += styleProp.charAt(i).toUpperCase();
            }
            else {
               fixedProp += styleProp.charAt(i);
            }
         }

         y = el.currentStyle[fixedProp];
      }
      else if (document.defaultView.getComputedStyle) {
         y = document.defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
      }
      return y;
   },

   _autoScale: function (thumbImg) {
      var container = this.get_element();
      var thumb = thumbImg.parentNode;
      var pageSize = this._targetControl.getPageSize(thumbImg.pageIndex);
      thumbImg.width = Math.floor(pageSize.width * this._scale);
      thumbImg.height = Math.floor(pageSize.height * this._scale);

      var scale = 1;
      if (this._autoScaleMode == 'vertical') {
         scale = (this._innerSize(container).width - this._chromeWidth(thumbImg) - this._chromeWidth(thumb)) / thumbImg.offsetWidth;
      }
      else if (this._autoScaleMode == 'horizontal') {
         scale = (this._innerSize(container).height - this._chromeHeight(thumbImg) - this._chromeHeight(thumb)) / thumbImg.offsetHeight;
      }

      thumbImg.width = Math.floor(parseInt(thumbImg.offsetWidth) * scale);
      thumbImg.height = Math.floor(parseInt(thumbImg.offsetHeight) * scale);
      //thumb.style.width = thumbImg.width + "px";
      //thumb.style.height = thumbImg.height + "px";
   },

   _loadHandler: function (e) {
      if (this._loading) {
         var loaded = this._loading;
         this._loading = null;
         $clearHandlers(loaded);
         this._autoScale(loaded);
      }
      this._loadNext();
   },

   _loadNext: function () {
      if (this._loading === null && this._loadQueue.length > 0) {
         while (this._loadQueue.length > 0) {
            var next = this._loadQueue.shift();
            if (!this._isLoaded(next)) {
               $addHandler(next, 'load', Function.createDelegate(this, this._loadHandler));

               var document = this._targetControl.get_document();

               var modifiedDate = null;

               if (document.LastModified instanceof Date)
                  modifiedDate = document.LastModified.getTime();
               else {
                  //in some cases the LastModified is not Date type but string
                  //which has a format '/Date(1245398693390)/'
                  //therefore we convert it to Date type here
                  var date = new Date(parseInt(document.LastModified.substr(6)));
                  modifiedDate = date.getTime();
               }

               var storageProviderName = (this._storageProviderName == null) ? "" : String.format("&sp={0}", this._storageProviderName);

               next.src = String.format(this._baseUrl, this._targetControl.get_document().StorageKey, this._scale, next.rotation, next.pageIndex, storageProviderName, modifiedDate);

               this._loading = next;
               break;
            }
         }
      }
   },

   _getThumbUrl: function (thumb, storageKey) {
      var document = this._targetControl.get_document();

      var modifiedDate = null;

      if (document.LastModified instanceof Date)
         modifiedDate = document.LastModified.getTime();
      else {
         //in some cases the LastModified is not Date type but string
         //which has a format '/Date(1245398693390)/'
         //therefore we convert it to Date type here
         var date = new Date(parseInt(document.LastModified.substr(6)));
         modifiedDate = date.getTime();
      }

      var storageProviderName = (this._storageProviderName == null) ? "" : String.format("&sp={0}", this._storageProviderName);
      return String.format(this._baseUrl, storageKey, this._scale, thumb.rotation, thumb.pageIndex, storageProviderName, modifiedDate);
   },

   _isLoaded: function (thumb) {
      return (thumb && thumb.src.indexOf(this._getThumbUrl(thumb, this._targetControl.get_document().StorageKey)) > 0);
   },

   get_targetControlId: function () {
      ///<summary>Gets or sets the control id of the associated PdfViewer control.</summary>
      ///<remarks>Setting this value after the control is initialized has no effect.</remarks>
      ///<value type="String" mayBeNull="false">The control id of the associated PdfViewer control.</value>
      return this._targetControlId;
   },

   set_targetControlId: function (value) {
      if (this._targetControlId != value) {
         this._targetControlId = value;
         this.raisePropertyChanged('targetControlId');
      }
   },

   get_baseUrl: function () {
      ///<summary>Gets or sets the base URL of the service that renders the thumbnails.</summary>
      ///<remarks>The URL should be formatted so that the page number may be appended to fetch a thumbnail for a specific page.</remarks>
      ///<value type="String" mayBeNull="false">The base URL of the service that renders the thumbnails.</value>
      return this._baseUrl;
   },

   set_baseUrl: function (value) {
      if (this._baseUrl != value) {
         this._baseUrl = value;
         this.raisePropertyChanged('baseUrl');
      }
   },

   get_activeCssClass: function () {
      /// <summary>
      /// Gets or sets the CSS class to be applied to the thumbnail that corresponds to the page currently showing in the the associated PdfViewer.
      /// </summary>
      /// <value type="String">The CSS class for the active thumbnail.</value>
      return this._activeCssClass;
   },

   set_activeCssClass: function (value) {
      if (this._activeCssClass != value) {
         this._activeCssClass = value;
         this.raisePropertyChanged('activeCssClass');
      }
   },


   get_autoScaleMode: function () {
      /// <summary>
      /// Gets or sets a value indicating whether to apply autoscaling to thumbnails (true) or not (false).
      /// </summary>
      /// <value type="Boolean">A value indicating whether to apply autoscaling to thumbnails</value>
      return this._autoScaleMode;
   },

   set_autoScaleMode: function (value) {
      if (this._autoScaleMode != value) {
         this._autoScaleMode = value;
         this.raisePropertyChanged('autoScaleMode');
      }
   },

   get_storageProviderName: function () {
      /// <summary>
      /// Gets or sets the name of the storage provider.
      /// </summary>
      return this._storageProviderName;
   },

   set_storageProviderName: function (value) {
      this._storageProviderName = value;
   },

   get_scale: function () {
      /// <summary>
      /// Gets or sets the default scale to apply to thumbnails.
      /// </summary>
      /// <value type="Number">The default scale for the thumbnails.</value>
      return this._scale;
   },

   set_scale: function (value) {
      if (value > 0 && value <= 0.25) {
         this._scale = value;
         this.raisePropertyChanged('scale');
      }
   }
};

// Optional descriptor for JSON serialization.
TallComponents.ThumbnailsList.descriptor = {
   properties: [{ name: "baseUrl", type: String },
                 { name: "targetControlId", type: String },
                 { name: "activeCssClass", type: String },
                 { name: "storageProviderName", type: String },
                 { name: "autoScaleMode", type: String}]
};
// Register the class as a type that inherits from Sys.UI.Control.
TallComponents.ThumbnailsList.registerClass('TallComponents.ThumbnailsList', Sys.UI.Control);

if (typeof (Sys) !== 'undefined') { Sys.Application.notifyScriptLoaded(); }